﻿using System;
using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using HandyControl.Tools.Interop;

namespace HandyControl.Data;

internal class GlowBitmap : DisposableObject
{
    internal const int GlowBitmapPartCount = 16;

    private const int BytesPerPixelBgra32 = 4;

    private static readonly CachedBitmapInfo[] _transparencyMasks = new CachedBitmapInfo[GlowBitmapPartCount];

    private readonly InteropValues.BITMAPINFO _bitmapInfo;

    private readonly IntPtr _pbits;

    internal GlowBitmap(IntPtr hdcScreen, int width, int height)
    {
        _bitmapInfo.biSize = Marshal.SizeOf(typeof(InteropValues.BITMAPINFOHEADER));
        _bitmapInfo.biPlanes = 1;
        _bitmapInfo.biBitCount = 32;
        _bitmapInfo.biCompression = 0;
        _bitmapInfo.biXPelsPerMeter = 0;
        _bitmapInfo.biYPelsPerMeter = 0;
        _bitmapInfo.biWidth = width;
        _bitmapInfo.biHeight = -height;

        Handle = InteropMethods.CreateDIBSection(
            hdcScreen,
            ref _bitmapInfo,
            0u,
            out _pbits,
            IntPtr.Zero,
            0u);
    }

    internal IntPtr Handle { get; }

    internal IntPtr DIBits => _pbits;

    internal int Width => _bitmapInfo.biWidth;

    internal int Height => -_bitmapInfo.biHeight;

    protected override void DisposeNativeResources() => InteropMethods.DeleteObject(Handle);

    private static byte PremultiplyAlpha(byte channel, byte alpha) => (byte) (channel * alpha / 255.0);

    internal static GlowBitmap Create(GlowDrawingContext drawingContext, GlowBitmapPart bitmapPart, Color color)
    {
        var orCreateAlphaMask =
            GetOrCreateAlphaMask(bitmapPart);

        var glowBitmap =
            new GlowBitmap(
                drawingContext.ScreenDC,
                orCreateAlphaMask.Width,
                orCreateAlphaMask.Height);

        for (var i = 0; i < orCreateAlphaMask.DIBits.Length; i += BytesPerPixelBgra32)
        {
            var b = orCreateAlphaMask.DIBits[i + 3];
            var val = PremultiplyAlpha(color.R, b);
            var val2 = PremultiplyAlpha(color.G, b);
            var val3 = PremultiplyAlpha(color.B, b);
            Marshal.WriteByte(glowBitmap.DIBits, i, val3);
            Marshal.WriteByte(glowBitmap.DIBits, i + 1, val2);
            Marshal.WriteByte(glowBitmap.DIBits, i + 2, val);
            Marshal.WriteByte(glowBitmap.DIBits, i + 3, b);
        }

        return glowBitmap;
    }

    private static CachedBitmapInfo GetOrCreateAlphaMask(GlowBitmapPart bitmapPart)
    {
        if (_transparencyMasks[(int) bitmapPart] == null)
        {
            var bitmapImage = new BitmapImage(new Uri($"pack://application:,,,/HandyControl;Component/Resources/Images/GlowWindow/{bitmapPart}.png"));

            var array = new byte[BytesPerPixelBgra32 * bitmapImage.PixelWidth * bitmapImage.PixelHeight];
            var stride = BytesPerPixelBgra32 * bitmapImage.PixelWidth;
            bitmapImage.CopyPixels(array, stride, 0);
            bitmapImage.Freeze();

            _transparencyMasks[(int) bitmapPart] =
                new CachedBitmapInfo(
                    array,
                    bitmapImage.PixelWidth,
                    bitmapImage.PixelHeight);
        }

        return _transparencyMasks[(int) bitmapPart];
    }

    private sealed class CachedBitmapInfo
    {
        internal readonly byte[] DIBits;
        internal readonly int Height;
        internal readonly int Width;

        internal CachedBitmapInfo(byte[] diBits, int width, int height)
        {
            Width = width;
            Height = height;
            DIBits = diBits;
        }
    }
}
